﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using YasharEl.Infrastructure.ApplicationModel.Modularity.Bootstrap;
using YasharEl.Infrastructure.ApplicationModel.Bootstrap.Configuration;
using YasharEl.Infrastructure.ServiceModel.Configuration;
using System.Configuration;
using System.ServiceModel.Configuration;
using YasharEl.Infrastructure.SoftwareModel.SOA;
using YasharEl.Infrastructure.ObjectFactory;

namespace YasharEl.Infrastructure.ApplicationModel.Bootstrap.Extensions
{
    public class ServiceModelBootstrapperExtension : ModuleComponentBootstrappingExtension, IConsumeConfigurationSection
    {
        #region Fields

        #endregion

        #region Properties

        public ServiceModelConfig ServiceModelConfigSection { get; private set; }

        #endregion

        #region IConsumeConfigurationSection Members

        public void Apply(ConfigurationSection section)
        {
            ServiceModelConfigSection = section as ServiceModelConfig;
        }

        #endregion

        #region ModuleComponentBootstrappingExtension Overrides

        public override string SectionName
        {
            get { return ServiceModelConfig.ServiceModelConfigDefaultSectionName; }
        }

        #endregion

        #region BaseComplexBootstrappingExtension Describe

        public override string Describe()
        {
            return "Bootstrapper extension which configure and register all services in application according to service taxonomy of SOA.";
        }

        #endregion

        #region BaseComplexBootstrappingExtension Configure, Start and Shutdown Methods

        public override void Configure()
        {
            base.Configure();

            try
            {
                if (ServiceModelConfigSection != null &&
                    ServiceModelConfigSection.Services != null)
                {
                    ServicesSection servicesSection = System.Configuration.ConfigurationManager.GetSection("system.serviceModel/services") as ServicesSection;

                    if (servicesSection == null)
                    {
                        Logger.Warn("system.serviceModel/services section not defined.");
                        return;
                    }

                    // Configure infrastructure services
                    ConfigureUtilityServices(ServiceModelConfigSection.Services, servicesSection);
                    ConfigureCommunicationServices(ServiceModelConfigSection.Services, servicesSection);

                    // Configure application services
                    ConfigureCapabilityServices(ServiceModelConfigSection.Services, servicesSection);
                    ConfigureEntityServices(ServiceModelConfigSection.Services, servicesSection);
                    ConfigureActivityServices(ServiceModelConfigSection.Services, servicesSection);
                    ConfigureProcessServices(ServiceModelConfigSection.Services, servicesSection);
                }
                else
                {
                    Logger.Warn("ServiceModelBootstrapperExtension registered but no configuration found related to the feature. Must use yasharEl.infrastructure.serviceModel section with defined services taxonomy inside.");
                }
            }
            catch (Exception ex)
            {
                Logger.Error(ex, "Error configuring service model feature.");
                throw new InvalidSystemConfigurationException("Error configuring service model feature.", ex);
            }
        }

        #endregion

        #region Private Configure Methods

        private void ConfigureUtilityServices(ServicesConfig servicesConfig, ServicesSection servicesSection)
        {
            Logger.Debug("Configuring utility services...");
            if (servicesConfig.InfrastructureServices != null &&
                servicesConfig.InfrastructureServices.UtilityServices != null &&
                servicesConfig.InfrastructureServices.UtilityServices.Count > 0)
            {
                foreach (UtilityServiceConfig utilityServiceConfig in servicesConfig.InfrastructureServices.UtilityServices)
                {
                    if (!servicesSection.Services.ContainsKey(utilityServiceConfig.ServiceName))
                    {
                        Logger.Warn("Service with name '{0}' not exists in system.serviceModel/services section and will ignored(not registered). Check you configuration files(s).", utilityServiceConfig.ServiceName);
                    }
                    else
                    {
                        Container.DependencyRegistrar.Register(
                            utilityServiceConfig.ServiceContractType,
                            utilityServiceConfig.ServiceImplementationType,
                            utilityServiceConfig.ServiceName
                        );
                        Container.DependencyRegistrar.Register(
                            utilityServiceConfig.ServiceContractType,
                            utilityServiceConfig.ServiceImplementationType
                        );
                        Container.DependencyRegistrar.Register(
                            typeof(IUtilityService),
                            utilityServiceConfig.ServiceImplementationType,
                            utilityServiceConfig.ServiceName
                        );

                        string serviceName = utilityServiceConfig.ServiceName;
                        Type serviceContractType = utilityServiceConfig.ServiceContractType;

                        if (Container.Parent != null)
                        {
                            FactoryMethod factoryMethod = new FactoryMethod(
                                (genericType, resolver, requestedType, key)
                                    =>
                                {
                                    return Container.DependencyResolver.Resolve(
                                        serviceContractType,
                                        serviceName
                                    );
                                }
                            );
                            Container.Parent.DependencyRegistrar.RegisterFactory(
                                utilityServiceConfig.ServiceContractType,
                                factoryMethod
                            );
                        }

                        Logger.Debug("Utility service with name '{0}' and type '{1}' registered...", utilityServiceConfig.ServiceName, utilityServiceConfig.ServiceContractType.FullNameToString());
                    }
                }
            }
            else
            {
                Logger.Debug("There no utility services to register.");
            }
        }

        private void ConfigureCommunicationServices(ServicesConfig servicesConfig, ServicesSection servicesSection)
        {
            Logger.Debug("Configuring communication services...");
            if (servicesConfig.InfrastructureServices != null &&
                servicesConfig.InfrastructureServices.CommunicationServices != null &&
                servicesConfig.InfrastructureServices.CommunicationServices.Count > 0)
            {
                foreach (CommunicationServiceConfig communicationServiceConfig in servicesConfig.InfrastructureServices.CommunicationServices)
                {
                    if (!servicesSection.Services.ContainsKey(communicationServiceConfig.ServiceName))
                    {
                        Logger.Warn("Service with name '{0}' not exists in system.serviceModel/services section and will ignored(not registered). Check you configuration files(s).", communicationServiceConfig.ServiceName);
                    }
                    else
                    {
                        Container.DependencyRegistrar.Register(
                            communicationServiceConfig.ServiceContractType,
                            communicationServiceConfig.ServiceImplementationType,
                            communicationServiceConfig.ServiceName
                        );
                        Container.DependencyRegistrar.Register(
                            communicationServiceConfig.ServiceContractType,
                            communicationServiceConfig.ServiceImplementationType
                        );
                        Container.DependencyRegistrar.Register(
                            typeof(ICommunicationService),
                            communicationServiceConfig.ServiceImplementationType,
                            communicationServiceConfig.ServiceName
                        );

                        string serviceName = communicationServiceConfig.ServiceName;
                        Type serviceContractType = communicationServiceConfig.ServiceContractType;

                        if (Container.Parent != null)
                        {
                            FactoryMethod factoryMethod = new FactoryMethod(
                                (genericType, resolver, requestedType, key)
                                    =>
                                {
                                    return Container.DependencyResolver.Resolve(
                                        communicationServiceConfig.ServiceContractType,
                                        serviceName
                                    );
                                }
                            );
                            Container.Parent.DependencyRegistrar.RegisterFactory(
                                serviceContractType,
                                factoryMethod
                            );
                        }

                        Logger.Debug("Communication service with name '{0}' and type '{1}' registered...", communicationServiceConfig.ServiceName, communicationServiceConfig.ServiceContractType.FullNameToString());
                    }
                }
            }
            else
            {
                Logger.Debug("There no communication services to register.");
            }
        }

        private void ConfigureActivityServices(ServicesConfig servicesConfig, ServicesSection servicesSection)
        {
            Logger.Debug("Configuring activity services...");
            if (servicesConfig.ApplicationServices != null &&
                servicesConfig.ApplicationServices.ActivityServices != null &&
                servicesConfig.ApplicationServices.ActivityServices.Count > 0)
            {
                foreach (ActivityServiceConfig activityServiceConfig in servicesConfig.ApplicationServices.ActivityServices)
                {
                    if (!servicesSection.Services.ContainsKey(activityServiceConfig.ServiceName))
                    {
                        Logger.Warn("Service with name '{0}' not exists in system.serviceModel/services section and will ignored(not registered). Check you configuration files(s).", activityServiceConfig.ServiceName);
                    }
                    else
                    {
                        Container.DependencyRegistrar.Register(
                            activityServiceConfig.ServiceContractType,
                            activityServiceConfig.ServiceImplementationType,
                            activityServiceConfig.ServiceName
                        );
                        Container.DependencyRegistrar.Register(
                            activityServiceConfig.ServiceContractType,
                            activityServiceConfig.ServiceImplementationType
                        );
                        Container.DependencyRegistrar.Register(
                            typeof(IActivityService),
                            activityServiceConfig.ServiceImplementationType,
                            activityServiceConfig.ServiceName
                        );

                        string serviceName = activityServiceConfig.ServiceName;
                        Type serviceContractType = activityServiceConfig.ServiceContractType;

                        if (Container.Parent != null)
                        {
                            FactoryMethod factoryMethod = new FactoryMethod(
                                (genericType, resolver, requestedType, key)
                                    =>
                                {
                                    return Container.DependencyResolver.Resolve(
                                        serviceContractType,
                                        serviceName
                                    );
                                }
                            );
                            Container.Parent.DependencyRegistrar.RegisterFactory(
                                activityServiceConfig.ServiceContractType,
                                factoryMethod
                            );
                        }

                        Logger.Debug("Activity service with name '{0}' and type '{1}' registered...", activityServiceConfig.ServiceName, activityServiceConfig.ServiceContractType.FullNameToString());
                    }
                }
            }
            else
            {
                Logger.Debug("There no activity services to register.");
            }
        }

        private void ConfigureProcessServices(ServicesConfig servicesConfig, ServicesSection servicesSection)
        {
            Logger.Debug("Configuring process services...");
            if (servicesConfig.ApplicationServices != null &&
                servicesConfig.ApplicationServices.ProcessServices != null &&
                servicesConfig.ApplicationServices.ProcessServices.Count > 0)
            {
                foreach (ProcessServiceConfig processServiceConfig in servicesConfig.ApplicationServices.ProcessServices)
                {
                    if (!servicesSection.Services.ContainsKey(processServiceConfig.ServiceName))
                    {
                        Logger.Warn("Service with name '{0}' not exists in system.serviceModel/services section and will ignored(not registered). Check you configuration files(s).", processServiceConfig.ServiceName);
                    }
                    else
                    {
                        Container.DependencyRegistrar.Register(
                            processServiceConfig.ServiceContractType,
                            processServiceConfig.ServiceImplementationType,
                            processServiceConfig.ServiceName
                        );
                        Container.DependencyRegistrar.Register(
                            processServiceConfig.ServiceContractType,
                            processServiceConfig.ServiceImplementationType
                        );
                        Container.DependencyRegistrar.Register(
                            typeof(IProcessService),
                            processServiceConfig.ServiceImplementationType,
                            processServiceConfig.ServiceName
                        );

                        string serviceName = processServiceConfig.ServiceName;
                        Type serviceContractType = processServiceConfig.ServiceContractType;

                        if (Container.Parent != null)
                        {
                            FactoryMethod factoryMethod = new FactoryMethod(
                                (genericType, resolver, requestedType, key)
                                    =>
                                {
                                    return Container.DependencyResolver.Resolve(
                                        serviceContractType,
                                        serviceName
                                    );
                                }
                            );
                            Container.Parent.DependencyRegistrar.RegisterFactory(
                                processServiceConfig.ServiceContractType,
                                factoryMethod
                            );
                        }

                        Logger.Debug("Process service with name '{0}' and type '{1}' registered...", processServiceConfig.ServiceName, processServiceConfig.ServiceContractType.FullNameToString());
                    }
                }
            }
            else
            {
                Logger.Debug("There no process services to register.");
            }
        }

        private void ConfigureCapabilityServices(ServicesConfig servicesConfig, ServicesSection servicesSection)
        {
            Logger.Debug("Configuring capability services...");
            if (servicesConfig.ApplicationServices != null &&
                servicesConfig.ApplicationServices.CapabilityServices != null &&
                servicesConfig.ApplicationServices.CapabilityServices.Count > 0)
            {
                foreach (CapabilityServiceConfig capabilityServiceConfig in servicesConfig.ApplicationServices.CapabilityServices)
                {
                    if (!servicesSection.Services.ContainsKey(capabilityServiceConfig.ServiceName))
                    {
                        Logger.Warn("Service with name '{0}' not exists in system.serviceModel/services section and will ignored(not registered). Check you configuration files(s).", capabilityServiceConfig.ServiceName);
                    }
                    else
                    {
                        Container.DependencyRegistrar.Register(
                            capabilityServiceConfig.ServiceContractType,
                            capabilityServiceConfig.ServiceImplementationType,
                            capabilityServiceConfig.ServiceName
                        );
                        Container.DependencyRegistrar.Register(
                            capabilityServiceConfig.ServiceContractType,
                            capabilityServiceConfig.ServiceImplementationType
                        );
                        Container.DependencyRegistrar.Register(
                            typeof(ICapabilityService),
                            capabilityServiceConfig.ServiceImplementationType,
                            capabilityServiceConfig.ServiceName
                        );

                        string serviceName = capabilityServiceConfig.ServiceName;
                        Type serviceContractType = capabilityServiceConfig.ServiceContractType;

                        if (Container.Parent != null)
                        {
                            FactoryMethod factoryMethod = new FactoryMethod(
                                (genericType, resolver, requestedType, key)
                                    =>
                                {
                                    return Container.DependencyResolver.Resolve(
                                        serviceContractType,
                                        serviceName
                                    );
                                }
                            );
                            Container.Parent.DependencyRegistrar.RegisterFactory(
                                capabilityServiceConfig.ServiceContractType,
                                factoryMethod
                            );
                        }

                        Logger.Debug("Capability service with name '{0}' and type '{1}' registered...", capabilityServiceConfig.ServiceName, capabilityServiceConfig.ServiceContractType.FullNameToString());
                    }
                }
            }
            else
            {
                Logger.Debug("There no capability services to register.");
            }
        }

        private void ConfigureEntityServices(ServicesConfig servicesConfig, ServicesSection servicesSection)
        {
            Logger.Debug("Configuring entity services...");
            if (servicesConfig.ApplicationServices != null &&
                servicesConfig.ApplicationServices.EntityServices != null &&
                servicesConfig.ApplicationServices.EntityServices.Count > 0)
            {
                foreach (EntityServiceConfig entityServiceConfig in servicesConfig.ApplicationServices.EntityServices)
                {
                    if (!servicesSection.Services.ContainsKey(entityServiceConfig.ServiceName))
                    {
                        Logger.Warn("Service with name '{0}' not exists in system.serviceModel/services section and will ignored(not registered). Check you configuration files(s).", entityServiceConfig.ServiceName);
                    }
                    else
                    {
                        Container.DependencyRegistrar.Register(
                            entityServiceConfig.ServiceContractType,
                            entityServiceConfig.ServiceImplementationType,
                            entityServiceConfig.ServiceName
                        );
                        Container.DependencyRegistrar.Register(
                            entityServiceConfig.ServiceContractType,
                            entityServiceConfig.ServiceImplementationType
                        );
                        Container.DependencyRegistrar.Register(
                            typeof(IEntityService),
                            entityServiceConfig.ServiceImplementationType,
                            entityServiceConfig.ServiceName
                        );

                        string serviceName = entityServiceConfig.ServiceName;
                        Type serviceContractType = entityServiceConfig.ServiceContractType;

                        if (Container.Parent != null)
                        {
                            FactoryMethod factoryMethod = new FactoryMethod(
                                (genericType, resolver, requestedType, key)
                                    =>
                                {
                                    return Container.DependencyResolver.Resolve(
                                        serviceContractType,
                                        serviceName
                                    );
                                }
                            );
                            Container.Parent.DependencyRegistrar.RegisterFactory(
                                entityServiceConfig.ServiceContractType,
                                factoryMethod
                            );
                        }

                        Logger.Debug("Entity service with name '{0}' and type '{1}' registered...", entityServiceConfig.ServiceName, entityServiceConfig.ServiceContractType.FullNameToString());
                    }
                }
            }
            else
            {
                Logger.Debug("There no entity services to register.");
            }
        }

        #endregion
    }
}
